package be.ac.ua.bank;

import java.io.IOException;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.UUID;
import java.util.Vector;

import be.ac.ua.codebase.BankAccount;
import be.ac.ua.codebase.BankAccountInterface;
import be.ac.ua.codebase.BankInterface;


import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.ConfigurationProvider;
import net.jini.core.entry.Entry;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.UnknownEventException;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.transaction.CannotJoinException;
import net.jini.core.transaction.UnknownTransactionException;
import net.jini.core.transaction.server.CrashCountException;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.transaction.server.TransactionParticipant;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.export.Exporter;
import net.jini.jeri.BasicILFactory;
import net.jini.jeri.BasicJeriExporter;
import net.jini.jeri.tcp.TcpServerEndpoint;
import net.jini.lookup.entry.Name;


public class BankImplementation implements DiscoveryListener, BankInterface {
	private Vector<ServiceRegistrar> fRegistrars = new Vector<ServiceRegistrar>();
	private HashMap<UUID, BankAccountInterface> fAccounts = new HashMap<UUID, BankAccountInterface>();
	private String fName = "BankImplementation";
	
	public BankImplementation() throws IOException {
		//Enable lookup discovery
		String[] groups = new String[] {"NDFLEXTRADE"};
		LookupDiscoveryManager lookupDiscovery = new LookupDiscoveryManager(groups, null, this);
	}
	
	public TransactionManager getTransactionManager() throws RemoteException {
		TransactionManager transactionManager = null;
		
		Entry[] attributes = new Entry[1];
		attributes[0] = new Name ("Transaction Manager");
		Class[] classes = new Class[] {TransactionManager.class};
		
		for (int i = 0; i < fRegistrars.size(); i++) {			
			TransactionManager tmp = (TransactionManager) fRegistrars.get(i).lookup(new ServiceTemplate(null, classes, null));
			if (tmp != null) {
				transactionManager = tmp;
			}
		}
		
		if (transactionManager == null)
			System.out.println("[ERROR] - TransactionManager not found!");
		
		return transactionManager;
	}
	
	public void setName(String name) {
		fName = name;
	}
	
	public String getName() throws RemoteException {
		return fName;
	}
	
	public UUID register(String firstName, String familyName, int postalCode) throws RemoteException, ConfigurationException {
		BankAccountInterface bankAccount = new BankAccount(firstName, familyName, postalCode);
		Exporter exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory());
		BankAccountInterface proxy = (BankAccountInterface) exporter.export(bankAccount);
		fAccounts.put(bankAccount.getId(), proxy);
		System.out.println("[BANK] - Registered account for " + firstName + " " + familyName + " with ID: " + bankAccount.getId().toString());
		return bankAccount.getId();
	}
	
	public UUID register(String firstName, String familyName, String postalCode) throws RemoteException, ConfigurationException {
		BankAccountInterface bankAccount = new BankAccount(firstName, familyName, postalCode);
		Exporter exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory());
		BankAccountInterface proxy = (BankAccountInterface) exporter.export(bankAccount);
		fAccounts.put(bankAccount.getId(), proxy);
		System.out.println("[BANK] - Registered account for " + firstName + " " + familyName + " with ID: " + bankAccount.getId().toString());
		return bankAccount.getId();
	}
	
	public double getAmount(UUID accountId) throws RemoteException {
		BankAccountInterface account = fAccounts.get(accountId);
		return account.getAmount();
	}
	
	public void credit(UUID accountId, double amount) throws RemoteException {
		System.out.println("[BANK] - Credit " + amount);
		fAccounts.get(accountId).credit(amount);
	}
	
	public void debit(UUID accountId, double amount) throws RemoteException {
		System.out.println("[BANK] - Debit " + amount);
		fAccounts.get(accountId).debit(amount);
	}

	public void credit(UUID accountId, double amount, long transactionId) throws RemoteException, UnknownTransactionException, CannotJoinException, CrashCountException, RemoteException  {
		BankAccountInterface account = fAccounts.get(accountId);
		account.credit(amount, this.getTransactionManager(), transactionId);
	}

	public void debit(UUID accountId, double amount, long transactionId) throws RemoteException, UnknownTransactionException, CannotJoinException, CrashCountException {
		BankAccountInterface account = fAccounts.get(accountId);	
		account.debit(amount, this.getTransactionManager(), transactionId);
	}

	public void discarded(DiscoveryEvent evt) {
		ServiceRegistrar[] registrars = evt.getRegistrars();
		for (int n = 0; n < registrars.length; n++) { 
			fRegistrars.remove(registrars[n]);
		}		
	}

	public void discovered(DiscoveryEvent evt) { 
		ServiceRegistrar[] registrars = evt.getRegistrars();
		for (int n = 0; n < registrars.length; n++) { 
			fRegistrars.add(registrars[n]);
		}
	}
}
